#pragma once
#include <iostream>
#include <functional>
#include <string>
#include <fstream>
#include <iterator>
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <unordered_set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <sys/stat.h>
#include "fileoperator.hpp"
#include <jsoncpp/json/json.h>
#include "util.hpp"
#include "dboperator.hpp"
#include "session.hpp"
#include "online.hpp"
#include "matcher.hpp"
#include "room.hpp"
#define HTTPPAGE "./wwwroot/index.html"

std::unordered_map<std::string, std::string> mimeTypes = {
    {".ico", "image/x-icon"},
    {".png", "image/png"},
    {".jpg", "image/jpeg"},
    {".jpeg", "image/jpeg"},
    {".svg", "image/svg+xml"},
    {".html", "text/html; charset=utf-8"},  // 添加字符集
    {".css", "text/css; charset=utf-8"},
    {".js", "application/javascript; charset=utf-8"},
    {".txt", "text/plain; charset=utf-8"}   // 补充常见文本类型
};
typedef websocketpp::server<websocketpp::config::asio> server;
class WebsocketServer{
public:
    WebsocketServer(): _sessionmanager(&_server)
    {
        //初始化端节点
        _server.init_asio();
        //清空日志信息
        _server.clear_access_channels(websocketpp::log::alevel::all);
        _server.clear_error_channels(websocketpp::log::elevel::all);
        //注册连接处理程序
        //当HTTP请求来时我的处理
        _server.set_http_handler(std::bind(&WebsocketServer::onHttp,this,std::placeholders::_1));
        //当Websocket连接请求来时我的处理
        _server.set_open_handler(std::bind(&WebsocketServer::onOpen,this,std::placeholders::_1));
        //当message来时我的处理
        _server.set_message_handler(std::bind(&WebsocketServer::onMessage,this,std::placeholders::_1,std::placeholders::_2));
        //当连接关闭时我的处理
        _server.set_close_handler(std::bind(&WebsocketServer::onClose,this,std::placeholders::_1));
        //初始化mysql
        Sql::getinstance()->init();
    }
    void run(uint16_t port){
        _server.listen(port);
        std::cout<<"监听成功"<<std::endl;
        _server.start_accept();
        std::cout<<"accept 成功"<<std::endl;
        _server.run(); //调用 run 后，事件循环会持续处理
    }



    void onHttp(websocketpp::connection_hdl hdl) {
        std::cout << "http handler" << std::endl;
        server::connection_ptr con = _server.get_con_from_hdl(hdl);
        std::string url = con->get_request().get_uri();
        if(url == "/"){
            //访问登录页面页面
            http_response(con,websocketpp::http::status_code::ok,Fileoperator::readfile(HTTPPAGE));
        }else if(url == "/register"){
            reg(con);
        }else{
            std::string path = "./wwwroot"+url;
            if(Fileoperator::is_existent(path)){
                http_response(con,websocketpp::http::status_code::ok,Fileoperator::readfile(path));
            }else if(url == "/login"){
                login(con);
            }else if(url == "/info"){
                info(con);
            }else{
                path = "./wwwroot/404.html";
                http_response(con,websocketpp::http::status_code::not_found,Fileoperator::readfile(path));
            }
            
        }
    }
    void onOpen(websocketpp::connection_hdl hdl ){
        server::connection_ptr con = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = con->get_request();
        std::string uri = req.get_uri();
        //TODO
        if(uri == "/hall"){
            //建立了大厅的长连接
            return ws_onopen_gamehall(con);
        }
        if(uri == "/room"){
            //TODO
            return ws_onopen_gameroom(con);
        }
    }
    void onMessage(websocketpp::connection_hdl hdl,server::message_ptr msg){
        server::connection_ptr con = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = con->get_request();
        std::string uri = req.get_uri();
        if(uri == "/hall"){
            //大厅的长连接
            return ws_onmessage_gamehall(con,msg);
        }
        if(uri == "/room"){
            return ws_onmessage_gameroom(con,msg);
        }
    }
    void onClose(websocketpp::connection_hdl hdl){
        server::connection_ptr con = _server.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req = con->get_request();
        std::string uri = req.get_uri();
        //TODO
        if(uri == "/hall"){
            //关闭大厅的长连接
            return ws_onclose_gamehall(con);
        }else if(uri == "/room"){
            return ws_onclose_gameroom(con);
        }
    }

private:
    void ws_onclose_gameroom(server::connection_ptr con){
        //验证身份
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            return;
        }
        //将玩家从room在线中移除，重新设置session时效，将玩家从房间移除
        uint64_t uid = session->getuid();
        online_manager::get_online_manager()->exit_from_gameroom(uid);
        _sessionmanager.set_session_expire_time(session->getssid(),SESSION_TIMEOUT);
        room_manager::get_instance()->remove_user(uid);
        return;
    }
    void ws_onmessage_gameroom(server::connection_ptr con,server::message_ptr msg){
        //身份认证
        Json::Value res;
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            return;
        }
        //获取房间信息
        //1.根据uid获取房间信息
        room_ptr rp = room_manager::get_instance()->get_room_by_uid(session->getuid());
        if(rp.get() == nullptr){
            res["optype"] = "unknow";
            res["reason"] = "未找到房间信息";
            res["result"] = false;
            return ws_response(con,res);
        }
        //2.对消息反序列化
        Json::Value request_json;
        std::string req_string = msg->get_payload();
        bool ret = JsonUtil::deserialization(req_string,request_json);
        if(!ret){
            res["optype"] = "unknow";
            res["reason"] = "反序列化失败";
            res["result"] = false;
            return ws_response(con,res);
        }
        return rp->handler_request(request_json);

    }
    void ws_onopen_gameroom(server::connection_ptr con){
        //1.获取session
        logmessage(DEBUG,"调用成功");
        Json::Value res;
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            logmessage(DEBUG,"session为空");
            return;
        }
        //2.判断是否创建好房间
        room_ptr rp = room_manager::get_instance()->get_room_by_uid(session->getuid());
        if(rp.get() == nullptr){
            //房间未创建
            res["optype"] = "room_ready";
            res["reason"] = "没有找到房间信息";
            res["result"] = false;
            logmessage(DEBUG,"123");
            return ws_response(con,res);
        }
        online_manager::get_online_manager()->exit_from_gamehall(session->getuid());
        online_manager::get_online_manager()->insert_in_gameroom(session->getuid(),con);
        _sessionmanager.set_session_expire_time(session->getssid(),SESSION_FOREVER);
        //返回房间准备完毕信息
        res["optype"] = "room_ready";
        res["result"] = true;
        res["room_id"] = (Json::UInt64)rp->get_room_id();
        res["uid"] = (Json::UInt64)session->getuid();
        res["white_id"] = (Json::UInt64)rp->get_white_user();
        res["black_id"] = (Json::UInt64)rp->get_black_user();
        logmessage(DEBUG,"返回信息成功");
        return ws_response(con,res);
        
    }
    void ws_onmessage_gamehall(server::connection_ptr con,server::message_ptr msg){
        //验证session的合法性
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            return;
        }
        //获取信息内容
        std::string data = msg->get_payload();
        Json::Value jv;
        bool ret = JsonUtil::deserialization(data,jv);
        Json::Value sendinfo;
        if(ret == false){
            sendinfo["result"] = false;
            sendinfo["reason"] = "反序列化失败";
            sendinfo["optype"] = "hall_ready";
            ws_response(con,sendinfo);
            return;
        }
        //根据optype进行处理
        if(!jv["optype"].isNull() && jv["optype"].asString() == "match_start" ){
            _matcher_manager.add(session->getuid());
            sendinfo["result"] = "true";
            sendinfo["reason"] = "正在匹配";
            sendinfo["optype"] = "match_start";
            ws_response(con,sendinfo);
            return;
        }else if(!jv["optype"].isNull() && jv["optype"].asString() == "match_stop"){
            //TODO
            _matcher_manager.del(session->getuid());
            sendinfo["result"] = "true";
            sendinfo["reason"] = "取消匹配";
            sendinfo["optype"] = "match_stop";
            ws_response(con,sendinfo);
            return;
        }
        sendinfo["result"] = false;
        sendinfo["reason"] = "未知的optype";
        sendinfo["optype"] = "undefine";
        ws_response(con,sendinfo);
        logmessage(ERROR,"未知的optype:%s",jv["optype"].asString().c_str());
        return;
    }
    void ws_onclose_gamehall(server::connection_ptr con){
        //验证是否登录
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            return;
        }
        //logmessage(DEBUG,"1");
        online_manager::get_online_manager()->exit_from_gamehall(session->getuid());
        online_manager::get_online_manager()->exit_from_gameroom(session->getuid());
        logmessage(DEBUG,"2");
        _sessionmanager.set_session_expire_time(session->getssid(),SESSION_TIMEOUT);
        //logmessage(DEBUG,"3");

        //logmessage(NORMAL,"用户退出清理成功");
    }
    void ws_response(server::connection_ptr con,Json::Value& jv){
        std::string data;
        JsonUtil::serialization(data,jv);
        con->send(data);
    }
    std::shared_ptr<Session> get_session_from_con(server::connection_ptr con){
        //1.从con中获取ssid
        Json::Value sendinfo;
        std::string cookie_str = con->get_request_header("Cookie");
        std::string ssid_str;
        bool ret = get_ssid_from_cookie(cookie_str,ssid_str);
        if(ret == false || cookie_str.empty()){
            sendinfo["result"] = false;
            sendinfo["reason"] = "cookie信息未找到";
            sendinfo["optype"] = "hall_ready";
            return std::shared_ptr<Session>();
        }

        //2.ssid -》session
        std::shared_ptr<Session> session = _sessionmanager.find_session(std::stol(ssid_str));
        return session;
    }
    void ws_onopen_gamehall(server::connection_ptr con){
        //验证session   TODO
        //从句柄中获取session
        Json::Value sendinfo;
        std::shared_ptr<Session> session = get_session_from_con(con);
        if(session.get() == nullptr){
            sendinfo["result"] = false;
            sendinfo["reason"] = "session信息未找到";
            sendinfo["optype"] = "hall_ready";
            ws_response(con,sendinfo);
        }
        //session合法
        //如果已经有用户登录了，我不管继续登录，挤掉之前的用户,因为我在createsession的时候就将之前的ssid删除了
        online_manager::get_online_manager()->insert_in_gamehall(session->getuid(),con);

        sendinfo["result"] = true;
        sendinfo["reason"] = "连接到游戏大厅成功";
        sendinfo["optype"] = "hall_ready";

        _sessionmanager.set_session_expire_time(session->getssid(),SESSION_FOREVER);

        ws_response(con,sendinfo);
        

    }
    // bool get_ssid_from_cookie(std::string cookie_str,std::string& ssid_str){
    //     //cookie_str  SSID=1
    //     logmessage(DEBUG,"进入 get_ssid_from_cookie");
    //     //logmessage(DEBUG,"cookie_str%s",cookie_str.c_str());
    //     auto pos = cookie_str.find('=');
    //     if(pos == std::string::npos){
    //         return false;
    //     }
    //     ssid_str = cookie_str.substr(pos+1);
    //     logmessage(DEBUG,"出 get_ssid_from_cookie");
    //     if(ssid_str.empty())    return false;
    //     return true;
    // }
    bool get_ssid_from_cookie(const std::string& cookie_str, std::string& ssid_str) {
        logmessage(DEBUG, "进入 get_ssid_from_cookie");
        
        // 避免空字符串处理
        if (cookie_str.empty()) {
            logmessage(DEBUG, "cookie_str 为空");
            return false;
        }
        
        // 查找 '=' 位置
        size_t pos = cookie_str.find('=');
        if (pos == std::string::npos) {
            logmessage(DEBUG, "cookie_str 中未找到 '='");
            return false;
        }
        
        // 检查 pos+1 是否越界（避免 substr 抛出异常）
        if (pos + 1 > cookie_str.size()) {
            logmessage(DEBUG, "SSID 值位置越界");
            return false;
        }
        
        // 提取 SSID 值
        ssid_str = cookie_str.substr(pos + 1);
        
        // 可选：如果 SSID 不允许为空，可增加检查
        if (ssid_str.empty()) {
            logmessage(DEBUG, "提取的 SSID 为空");
            return false; // 根据业务需求决定是否返回 false
        }
        
        logmessage(DEBUG, "出 get_ssid_from_cookie，提取的 SSID: %s", ssid_str.c_str());
        return true;
    }
    void info(server::connection_ptr con){
        //验证cookie的合法性
        logmessage(DEBUG,"进入info");
        std::string cookie_str = con->get_request_header("Cookie");
        if(cookie_str.empty()){
            return http_response(con,websocketpp::http::status_code::bad_request,"Cookie为空,请重新登录");
        }
        std::string ssid_str;
        //logmessage(DEBUG,"Cookie:%s",cookie_str.c_str());//崩溃在这里之后
        bool ret = get_ssid_from_cookie(cookie_str,ssid_str);
        if(ret ==false){
            return http_response(con,websocketpp::http::status_code::bad_request,"SSID为空,请重新登录");
        }
        std::shared_ptr<Session> session = _sessionmanager.find_session(std::stol(ssid_str));
        if(session.get() == nullptr){
            return http_response(con,websocketpp::http::status_code::bad_request,"会话已失效,请重新登录");
        }
        //利用ssid找到uid
        uint64_t uid = session->getuid();
        //再从数据库中获取用户信息
        Json::Value userinfo;
        std::string errinfo;
        int r = Sql::getinstance()->getuserinfos(uid,userinfo,errinfo);
        if(r == false){
            return http_response(con,websocketpp::http::status_code::bad_request,errinfo.c_str());
        }

        std::string jv;
        JsonUtil::serialization(jv,userinfo);
        //logmessage(DEBUG,"JV:%s",jv.c_str());
        con->append_header("Content-Type", "application/json");
        //设置session过期时间
        _sessionmanager.set_session_expire_time(std::stol(ssid_str),SESSION_TIMEOUT);
        return http_response(con,websocketpp::http::status_code::ok,jv);
        

    }
    void login(server::connection_ptr con){
        std::string body = con->get_request_body();
        Json::Value login_info;
        bool is_ok = JsonUtil::deserialization(body,login_info);
        if(is_ok){
            if(login_info["username"].isNull() || login_info["username"].asString().empty()
            || login_info["password"].isNull() || login_info["password"].asString().empty()){
                logmessage(ERROR,"登录信息不能为空");
                http_response(con,websocketpp::http::status_code::bad_request,"登录信息不能为空");
            }else{
                //登录逻辑，与后台数据库的数据比对
                logmessage(NORMAL,"正在登录用户");
                std::string errinfo;
                std::string username = login_info["username"].asString();
                std::string password = login_info["password"].asString();
                Userinfos userinfos;
                bool ret = Sql::getinstance()->login(username,password,errinfo,userinfos);
                if(ret){
                    //登录成功
                    // http_response(con,websocketpp::http::status_code::ok,"登录成功");
                    //设置session 
                    uint64_t uid = userinfos.id;
                    std::shared_ptr<Session> session =  _sessionmanager.createsession(uid,LOGIN);
                    if(session.get() == nullptr){
                        logmessage(ERROR,"获取session失败");
                        http_response(con,websocketpp::http::status_code::bad_request,"获取session失败");
                    }
                    std::string cookie_ssid = "SSID="+std::to_string(session->getssid());
                    con->append_header("Set-Cookie",cookie_ssid);
                    http_response(con,websocketpp::http::status_code::ok,"登录成功");

                }else{
                    //登录失败
                    http_response(con,websocketpp::http::status_code::bad_request,errinfo.c_str());
                }
            }
        }else{
            logmessage(ERROR,"格式错误");
            http_response(con,websocketpp::http::status_code::bad_request,"登录json格式错误");
        }
    }
    void reg(server::connection_ptr con){
        std::string body = con->get_request_body();
        Json::Value reg_info;
        bool is_ok = JsonUtil::deserialization(body,reg_info);
        if(is_ok){
            // logmessage(DEBUG,"接收注册信息成功,%s,%s",reg_info["username"].asCString(),reg_info["password"].asCString());
            // http_response(con,websocketpp::http::status_code::ok,"");
            if(reg_info["username"].isNull() || reg_info["username"].asString().empty() 
            || reg_info["password"].isNull() || reg_info["password"].asString().empty()
            || reg_info["nickname"].isNull() || reg_info["nickname"].asString().empty()
            || reg_info["email"].isNull() || reg_info["email"].asString().empty()){
                logmessage(ERROR,"注册信息不能为空");
                http_response(con,websocketpp::http::status_code::bad_request,"注册信息不能为空");
            }else{
                //正常注册
                logmessage(NORMAL,"正在注册用户");
                std::string message;//注册失败的原因
                int ret = Sql::getinstance()->createuser(reg_info["username"].asCString(),reg_info["password"].asCString(),reg_info["email"].asCString(),reg_info["nickname"].asCString(),message);
                if(ret){
                    http_response(con,websocketpp::http::status_code::ok,"注册成功");
                }else{
                    http_response(con,websocketpp::http::status_code::bad_request,message.c_str());
                }
            }

        }else{
            logmessage(ERROR,"收注册信息格式失败");
            http_response(con,websocketpp::http::status_code::bad_request,"注册信息格式错误");
        }
    }
    void http_response(server::connection_ptr con,websocketpp::http::status_code::value code,std::string data){
        // if(path.size()){
        //     std::string ext = Fileoperator::getExtension(path);
        //     std::string mime = mimeTypes.count(ext) ? mimeTypes[ext] : "application/octet-stream";
            
        //     std::string content(Fileoperator::readfile(path));
        //     off_t file_size = Fileoperator::getfilesize(path);

        //     con->set_status(code);
        //     con->append_header("Content-Type", mime);
        //     con->append_header("Content-Length", std::to_string(file_size));
        //     con->set_body(content);
        // }else{
        //     con->set_status(code);
        //     con->set_body("");
        // }


        con->set_status(code);
        con->set_body(data);

    }
private:
    server _server;
    Sessionmanager _sessionmanager;
    matcher_manager _matcher_manager;
};